package com.lucky.common.exception;

import com.lucky.common.base.ResultVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.ModelAndView;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * --@RestControllerAdvice的特点：<br/>
 * --@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。<br/>
 * --@RestControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。<br/>
 * --@RestControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。<br/>
 * --@ExceptionHandler：用于指定异常处理方法。当与@RestControllerAdvice配合使用时，用于全局处理控制器里的异常。<br/>
 * --@InitBinder：用来设置WebDataBinder，用于自动绑定前台请求参数到Model中。<br/>
 * --@ModelAttribute：本来作用是绑定键值对到Model中，当与@ControllerAdvice配合使用时，可以让全局的@RequestMapping都能获得在此处设置的键值对<br/>
 */
@ControllerAdvice
public class GlobalExceptionHandler {

	public Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
	
	String err5xx = "error/5xx";

	@ExceptionHandler(value = TypeMismatchException.class)
	public ModelAndView exceptionHandle(TypeMismatchException e) {
		Map<String, String> errorMap = new HashMap<>();

		// 获取发生转换错误的字段名
		String fieldName = e.getPropertyName();
		// 获取目标类型（如Integer、Long等）
//		String targetType = e.getRequiredType().getSimpleName();
		// 获取错误的原始值
//		String invalidValue = e.getValue() != null ? e.getValue().toString() : "null";

		// 自定义错误消息
//		String errorMessage = String.format("字段 '%s' 转换失败，值 '%s' 不能转换为 %s 类型", fieldName, invalidValue, targetType);
		String errorMessage = "输入格式错误!!!";

		errorMap.put(fieldName, errorMessage);

		ModelAndView mv = new ModelAndView();
		mv.addObject("errMsg", "输入正确格式!!!");
		mv.addObject("resultVO", ResultVO.fail(errorMap));
		mv.setViewName("/error");

		return mv;
	}


	@ResponseBody
	@ExceptionHandler(value = BindException.class)
	public ModelAndView exceptionHandle(BindException exception) {
		BindingResult result = exception.getBindingResult();
		StringBuilder errorMsg = new StringBuilder();

		List<FieldError> fieldErrors = result.getFieldErrors();
        for (FieldError error : fieldErrors) {//			String field = error.getField();
            String message = error.getDefaultMessage();
            // 判断是否为类型转换错误（通过错误码或消息特征）
            if (error.getCode().contains("typeMismatch")) {
                // 自定义类型转换错误消息
//				message = String.format("字段 '%s' 类型错误，需为 %s", field, fieldError.getRejectedValue().getClass().getSimpleName());
                message = "输入格式错误!!!";
            }
            errorMsg.append(message).append("!");
        }

        ModelAndView mv = new ModelAndView();
        mv.addObject("resultVO", ResultVO.fail(errorMsg.toString()));
        mv.addObject("errMsg", errorMsg);
        mv.setViewName("error");
		
		return mv;
	} 

	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	public ModelAndView exceptionHandle(MethodArgumentNotValidException exception) {
		String msg = exception.getMessage();
		BindingResult result = exception.getBindingResult();
		StringBuilder errorMsg = new StringBuilder();

		List<FieldError> fieldErrors = result.getFieldErrors();
		fieldErrors.forEach(error -> {
			log.error("field: " + error.getField() + ", msg:" + error.getDefaultMessage());
			errorMsg.append(error.getDefaultMessage()).append("!");
		});


	    ModelAndView mv = new ModelAndView();
        mv.addObject("errMsg", errorMsg.toString());
        mv.addObject("resultVO", ResultVO.fail(errorMsg.toString()));
        mv.setViewName(err5xx);
		
		return mv;
	}

	// 处理运行时异常
	@ExceptionHandler(Exception.class)
	public ModelAndView exceptionHandle(Exception e) {
		log.error(e.getMessage(), e);
		e.printStackTrace();

		ModelAndView mv = new ModelAndView();
		mv.addObject("errMsg", e.getMessage());
		mv.addObject("resultVO", ResultVO.fail(e.getMessage()));
		mv.setViewName(err5xx);

		return mv;
	}
	// 处理运行时异常
	@ExceptionHandler(AppException.class)
	public ModelAndView exceptionHandle(AppException e) {
		log.error(e.getMessage(), e);
		e.printStackTrace();

	    ModelAndView mv = new ModelAndView();
        mv.addObject("errMsg", e.getMessage());
        mv.addObject("resultVO", ResultVO.fail(e.getMessage()));
        mv.setViewName(err5xx);
		
		return mv;
	}

	// 处理运行时异常
	@ExceptionHandler(RuntimeException.class)
	public ModelAndView exceptionHandle(RuntimeException e) {
		log.error(e.getMessage(), e);
		e.printStackTrace();

	    ModelAndView mv = new ModelAndView();
        mv.addObject("errMsg", e.getMessage());
        mv.addObject("resultVO", ResultVO.fail(e.getMessage()));
        mv.setViewName(err5xx);
		
		return mv;
	}

//	@ResponseBody
	@ExceptionHandler(ResponseStatusException.class)
	public ModelAndView responseStatusExceptionException(ResponseStatusException e) {
		log.error(e.getMessage(), e);
		e.printStackTrace();
		ModelAndView mv = new ModelAndView();
		mv.addObject("msg", e.getMessage());
		mv.addObject("resultVO", ResultVO.fail(e.getMessage()));
		mv.setViewName("base/error/aidNotMatch");
		return mv;
	}
	
	 //（1）全局数据绑定
   //应用到所有@RequestMapping注解方法  
   //此处将键值对添加到全局，注解了@RequestMapping的方法都可以获得此键值对  
   @ModelAttribute
   public void addToken(ModelMap model) {   
//       model.addAttribute("msg", "此处将键值对添加到全局，注解了@RequestMapping的方法都可以获得此键值对");  
   }    
   
   //（2）全局数据预处理
   //应用到所有@RequestMapping注解方法，在其执行之前初始化数据绑定器  
   //用来设置WebDataBinder  
   /*
	在controler下注册的话仅仅对当前controller有效
	 */
//	@InitBinder
	public void initBinderDate(WebDataBinder binder) {
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		CustomDateEditor dateEditor = new CustomDateEditor(df, true);
		binder.registerCustomEditor(Date.class, dateEditor);
	}
	
//	@InitBinder
//	public void initBinderTime(WebDataBinder binder) {
//		DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//		CustomDateEditor dateEditor = new CustomDateEditor(df, true);
//		binder.registerCustomEditor(Date.class, dateEditor);
//	}
   
    
}

